package com.fh.shop.api.interceptor;

import com.alibaba.fastjson.JSON;
import com.fh.shop.api.annotation.NeedLogin;
import com.fh.shop.api.common.ResponseEnum;
import com.fh.shop.api.common.ServerResponse;
import com.fh.shop.api.common.SystemConst;
import com.fh.shop.api.exception.TokenException;
import com.fh.shop.api.member.vo.MemberVo;
import com.fh.shop.api.util.KeyUtil;
import com.fh.shop.api.util.Md5Util;
import com.fh.shop.api.util.RedisUtil;
import jdk.nashorn.internal.parser.Token;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpHeaders;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.util.Base64;

public class LoginInterceptor extends HandlerInterceptorAdapter {

    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 允许跨域
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, "*");
        // 允许客户端传递 自定义的头信息
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "x-auth,content-type");
        // 允许客户端发送请求方式的类型【默认情况下 get,post是没问题的；但对于其他请求类型需要特别允许】
        response.setHeader(HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "DELETE,PUT");
        // options请求 并不是 真实的请求，对于 options请求 拦截 return false
        String requestMethod = request.getMethod();
        if (requestMethod.equalsIgnoreCase("options")) {
            return false;
        }
        if (!(handler instanceof HandlerMethod)) {
            // 放行
            return true;
        }
        // 获取方法
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        Method method = handlerMethod.getMethod();
        // 判断方法上是否有注解
        if (!method.isAnnotationPresent(NeedLogin.class)) {
            // 没有注解，直接放行
            return true;
        }
        // 进行验证
        String header = request.getHeader("x-auth");
        // 非空验证
        if (StringUtils.isEmpty(header)) {
            throw new TokenException(ResponseEnum.TOKEN_IS_NULL);
        }
        // 验证格式 x.y
        // . 是正则表达式中的关键字,所以要进行转义 \\.
        String[] headers = header.split("\\.");
        if (headers.length != 2) {
            throw new TokenException(ResponseEnum.TOKEN_FORMAT_IS_ERROR);
        }
        // ======================验签
        String base64MemberJson = headers[0];
        String base64Sign = headers[1];
        // 解码后还是字符串
        // 字节数组 转 字符串 new String(字节数组)
        String memberJson = null;
        String sign = null;
        try {
            memberJson = new String(Base64.getDecoder().decode(base64MemberJson));
            sign = new String(Base64.getDecoder().decode(base64Sign));
        } catch (Exception e) {
            e.printStackTrace();
            throw new TokenException(ResponseEnum.TOKEN_DECODE_FAIL);
        }
        String newSign = Md5Util.sign(memberJson, SystemConst.SECRET);
        if (!newSign.equals(sign)) {
            throw new TokenException(ResponseEnum.TOKEN_VALIDATE_IS_ERROR);
        }
        // 转java对象
        MemberVo memberVo = JSON.parseObject(memberJson, MemberVo.class);
        Long id = memberVo.getId();
        // 超时判断
        String key = KeyUtil.buildMemberKey(id);
        if (!RedisUtil.exist(key)) {
            throw new TokenException(ResponseEnum.TOKEN_TIME_OUT);
        }
        // x-auth 中包含的uuid [客户端提交过来的]
        String uuid = memberVo.getUuid();
        // redis存储的uuid
        String redisUUID = RedisUtil.get(key);
        // x-auth 中包含的uuid 和 redis存储的uuid是否一致
        if (!uuid.equals(redisUUID)) {
            throw new TokenException(ResponseEnum.TOKEN_IS_MISMATH);
        }
        // 超时时间重置【续命】【登录成功】
        RedisUtil.expire(key, SystemConst.MEMBER_EXPIRE_TIME);
        // 将用户信息存入到request,方便后续使用
        request.setAttribute(SystemConst.CURR_MEMBER, memberVo);
        return true;
    }
}
